﻿using Callisto.Controls;
using Padsurface.App.UserControls;
using Padsurface.App.ViewModels;
using System;
using System.Threading;
using System.Threading.Tasks;
using Windows.ApplicationModel.Core;
using Windows.UI.Core;
using Windows.UI.Input;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;

namespace Padsurface.App.Views
{
    public sealed partial class PadPage : Page
    {
        private CancellationTokenSource _increasingCancellationTokenSource;
        private CancellationTokenSource _decreasingCancellationTokenSource;

        public PadViewModel ViewModel
        {
            get
            {
                return DataContext as PadViewModel;
            }
        }

        public PadPage()
        {
            this.InitializeComponent();

            DataContext = CompositionContainer.GetInstance<PadViewModel>();
        }

        private void TempoButtonTapped(object sender, TappedRoutedEventArgs e)
        {
            var tempoUserControl = new TempoUserControl();
            tempoUserControl.TempoIncreased += (s, args) => TempoChanged(tempoUserControl);
            tempoUserControl.TempoIncreasing += (s, args) => TempoIncreasing(tempoUserControl, args);
            tempoUserControl.TempoDecreased += (s, args) => TempoChanged(tempoUserControl);
            tempoUserControl.TempoDecreasing += (s, args) => TempoDecreasing(tempoUserControl, args);

            var bpmBinding = new Binding { Source = ViewModel.Bpm, Mode = BindingMode.OneWay };
            tempoUserControl.SetBinding(TempoUserControl.BpmProperty, bpmBinding);

            var increaseTempoCommandBinding = new Binding { Source = ViewModel.IncreaseBpmCommand, Mode = BindingMode.TwoWay };
            tempoUserControl.SetBinding(TempoUserControl.IncreaseTempoCommandProperty, increaseTempoCommandBinding);

            var decreaseTempoCommandBinding = new Binding { Source = ViewModel.DecreaseBpmCommand, Mode = BindingMode.TwoWay };
            tempoUserControl.SetBinding(TempoUserControl.DecreaseTempoCommandProperty, decreaseTempoCommandBinding);

            var flyout = new Flyout
            {
                Content = tempoUserControl,
                Margin = new Thickness(1, 1, 1, 1),
                Placement = PlacementMode.Top,
                PlacementTarget = sender as UIElement,
                IsOpen = true
            };
        }

        private void TempoIncreasing(TempoUserControl sender, HoldingRoutedEventArgs e)
        {
            if (e.HoldingState != HoldingState.Started)
            {
                if (_increasingCancellationTokenSource != null)
                    _increasingCancellationTokenSource.Cancel();

                return;
            }

            _increasingCancellationTokenSource = new CancellationTokenSource();

            Task.Factory.StartNew(async o =>
            {
                while (!_increasingCancellationTokenSource.IsCancellationRequested)
                {
                    await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                    {
                        if (!ViewModel.IncreaseBpmCommand.CanExecute(null))
                        {
                            _increasingCancellationTokenSource.Cancel();
                            return;
                        }

                        ViewModel.IncreaseBpmCommand.Execute(null);
                        sender.Bpm = ViewModel.Bpm;
                    });

                    await Task.Delay(TimeSpan.FromMilliseconds(100), _increasingCancellationTokenSource.Token);
                }
            }
            , _increasingCancellationTokenSource.Token);
        }

        private void TempoDecreasing(TempoUserControl sender, HoldingRoutedEventArgs e)
        {
            if (e.HoldingState != HoldingState.Started)
            {
                if (_decreasingCancellationTokenSource != null)
                    _decreasingCancellationTokenSource.Cancel();

                return;
            }

            _decreasingCancellationTokenSource = new CancellationTokenSource();

            Task.Factory.StartNew(async o =>
            {
                while (!_decreasingCancellationTokenSource.IsCancellationRequested)
                {
                    await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                    {
                        if (!ViewModel.DecreaseBpmCommand.CanExecute(null))
                        {
                            _decreasingCancellationTokenSource.Cancel();
                            return;
                        }

                        ViewModel.DecreaseBpmCommand.Execute(null);
                        sender.Bpm = ViewModel.Bpm;
                    });

                    await Task.Delay(TimeSpan.FromMilliseconds(100), _decreasingCancellationTokenSource.Token);
                }
            }
            , _decreasingCancellationTokenSource.Token);
        }

        /// <summary>
        /// Event handler is workaround for binding, which doesn't update source in metro due to limitations
        /// </summary>
        private void TempoChanged(TempoUserControl tempoUserControl)
        {
            tempoUserControl.Bpm = ViewModel.Bpm;
            tempoUserControl.IsIncreaseTempoEnabled = ViewModel.IncreaseBpmCommand.CanExecute(null);
            tempoUserControl.IsDecreaseTempoEnabled = ViewModel.DecreaseBpmCommand.CanExecute(null); 
        }
    }
}